﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using OpenTK;
using OpenTK.Graphics;

namespace LasRendering
{
    public static class PlyHelper
    {
        public static List<Vertex> ReadVertices(string filename, out Vector3 minPosition, out Vector3 maxPosition)
        {
            minPosition = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
            maxPosition = new Vector3(float.MinValue, float.MinValue, float.MinValue);

            using (StreamReader sr = new StreamReader(filename))
            {
                int numVertex;
                int numFace;
                ReadHeader(sr, out numVertex, out numFace);

                List<Vertex> vertexList = new List<Vertex>(numVertex);
                for (int i = 0; i < numVertex; i++)
                {
                    string s = sr.ReadLine();
                    Regex regex = new Regex(@"(\+|\-)? \d+ (\.\d+)? (e (\+|\-)? \d+)?", RegexOptions.IgnorePatternWhitespace);
                    MatchCollection matchColl = regex.Matches(s);
                    Debug.Assert(matchColl.Count >= 3);

                    Vertex vertex = new Vertex();
                    vertex.Color = new Color4(1f, 1f, 1f, 1f);
                    vertex.Normal = new Vector3();
                    vertex.Position.X = Convert.ToSingle(matchColl[0].Value);
                    vertex.Position.Y = Convert.ToSingle(matchColl[1].Value);
                    vertex.Position.Z = Convert.ToSingle(matchColl[2].Value);
                    vertex.Normal.X = Convert.ToSingle(matchColl[3].Value);
                    vertex.Normal.Y = Convert.ToSingle(matchColl[4].Value);
                    vertex.Normal.Z = Convert.ToSingle(matchColl[5].Value);
                    vertexList.Add(vertex);

                    minPosition.X = Math.Min(minPosition.X, vertex.Position.X);
                    minPosition.Y = Math.Min(minPosition.Y, vertex.Position.Y);
                    minPosition.Z = Math.Min(minPosition.Z, vertex.Position.Z);
                    maxPosition.X = Math.Max(maxPosition.X, vertex.Position.X);
                    maxPosition.Y = Math.Max(maxPosition.Y, vertex.Position.Y);
                    maxPosition.Z = Math.Max(maxPosition.Z, vertex.Position.Z);
                }

                return vertexList;
            }
        }

        private static void ReadHeader(StreamReader sr, out int numVertex, out int numFace)
        {
            string s;
            Regex regex;
            Match m;

            //ply
            s = sr.ReadLine();
            Debug.Assert(s == "ply");

            //format ascii 1.0           { ascii/binary, format version number }
            s = sr.ReadLine();
            Debug.Assert(s == "format ascii 1.0");

            //comment made by anonymous  { comments keyword specified, like all lines }
            //comment this file is a cube
            s = sr.ReadLine();
            while (s.StartsWith("comment"))
            {
                s = sr.ReadLine();
            }

            //element vertex ##          { define "vertex" element, 8 of them in file }
            regex = new Regex(@"element \s+ (?<type>vertex|face) \s+ (?<num>\d+)", RegexOptions.IgnorePatternWhitespace);
            m = regex.Match(s);
            Debug.Assert(m.Success);
            Debug.Assert(m.Groups["type"].Value == "vertex");
            numVertex = Convert.ToInt32(m.Groups["num"].Value);

            //property float32 x         { vertex contains float "x" coordinate }
            //property float32 y         { y coordinate is also a vertex property }
            //property float32 z         { z coordinate, too }
            s = sr.ReadLine();
            AssertFloatProperty(s, "x");
            s = sr.ReadLine();
            AssertFloatProperty(s, "y");
            s = sr.ReadLine();
            AssertFloatProperty(s, "z");

            s = sr.ReadLine();
            while (s.StartsWith("property"))
            {
                //Ignore other properties
                s = sr.ReadLine();
            }

            //element face ##             { there are 6 "face" elements in the file }
            m = regex.Match(s);
            Debug.Assert(m.Success);
            Debug.Assert(m.Groups["type"].Value == "face");
            numFace = Convert.ToInt32(m.Groups["num"].Value);

            //property list uchar int vertex_indices
            s = sr.ReadLine();
            AssertListProperty(s, null);

            //end_header
            s = sr.ReadLine();
            Debug.Assert(s == "end_header");
        }

        private static void AssertFloatProperty(string s, string propertyName)
        {
            Regex regex = new Regex(@"property \s+ (?<data_type>\w+) \s+ (?<property_name>\w+)$", RegexOptions.IgnorePatternWhitespace);
            Match m = regex.Match(s);
            Debug.Assert(m.Success);
            Debug.Assert(m.Groups["data_type"].Value.StartsWith("float"));
            if (!string.IsNullOrEmpty(propertyName))
            {
                Debug.Assert(m.Groups["property_name"].Value == propertyName);
            }
        }

        private static void AssertListProperty(string s, string propertyName)
        {
            Regex regex = new Regex(@"property \s+ list \s+ \w+ \s+ (?<data_type>\w+) \s+ (?<property_name>\w+)$", RegexOptions.IgnorePatternWhitespace);
            Match m = regex.Match(s);
            Debug.Assert(m.Success);
            if (!string.IsNullOrEmpty(propertyName))
            {
                Debug.Assert(m.Groups["property_name"].Value == propertyName);
            }
        }
    }
}
